package com.gooluke.mds.aop;

import com.gooluke.mds.common.annotation.FixedDataSource;
import com.gooluke.mds.datasource.DatasourceContextHolder;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * @author gooluke
 */
public class DataSourceAnnotationInterceptor implements MethodInterceptor {

    private static final Logger log = LoggerFactory.getLogger(DataSourceAnnotationInterceptor.class);

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        log.info("DataSourceAnnotationInterceptor invoke before...");
        String oldDatasource = DatasourceContextHolder.getDatasource();
        //这里把他的数据源切换到方法上注解的指定数据源
        Method method = invocation.getMethod();
        FixedDataSource annotation = getFixedDataSource(method);
        if (annotation == null) {
            throw new RuntimeException("can not find annotation @FixedDataSource");
        }

        String newDatasource = annotation.value();
        // 切换数据源
        DatasourceContextHolder.setDatasource(newDatasource);
        try {
            return invocation.proceed();
        } finally {
            //这里还原回之前的数据源
            if (annotation.needRecover()) {
                DatasourceContextHolder.setDatasource(oldDatasource);
            }
        }
    }

    private static FixedDataSource getFixedDataSource(Method method) {
        FixedDataSource annotation = method.getAnnotation(FixedDataSource.class) != null ?
                method.getAnnotation(FixedDataSource.class) :
                method.getDeclaringClass().getAnnotation(FixedDataSource.class);
        // 如果方法上和类上没有注解，则从接口上获取注解
        // 不建议在接口上加注解，因为如果实现类实现了多个接口，
        // 而这个接口和另一个接口存在同名同参数方法，可能会获取到另一个接口上的注解，导致业务出错
        if (annotation == null) {
            Class<?> targetClass = method.getDeclaringClass();
            for (Class<?> interfaces : targetClass.getInterfaces()) {
                try {
                    // 查找接口中与当前方法匹配的方法
                    Method ifaceMethod = interfaces.getMethod(method.getName(), method.getParameterTypes());
                    annotation = ifaceMethod.getAnnotation(FixedDataSource.class);
                    if (annotation != null) {
                        break;
                    }
                } catch (NoSuchMethodException e) {
                    // 如果接口中没有该方法，继续下一个接口
                    continue;
                }
            }
        }
        return annotation;
    }
}
